#include <sys/stat.h>
#include <sys/utsname.h>
#include <sstream>
#include <dirent.h>
#include <stdio.h>
#include <cstring>
#include <iostream>
#include "zuclog.h"
#include "config.h"
#include "diagosapi.h"

int32_t DiagOSAPI::Mkdir(const std::string& dirName)
{
    std::string strSysCmd = std::string("mkdir -p ").append(dirName);
    system(strSysCmd.c_str());
    int32_t ret = IsDirExists(dirName);
    zucInnerDbg("Mkdir: %s [%d]\n", dirName.c_str(), ret);
    return ret;
}

int32_t DiagOSAPI::CopyFile(const std::string& src, const std::string& dstDir)
{
    std::stringstream cmdStream;
    cmdStream << "for file in " << src << "\n";
    cmdStream << "do if [ -f \"$file\" ]\n";
    cmdStream << "then cp -f \"$file\" " << dstDir << "\n";
    cmdStream << "fi\n";
    cmdStream << "done";
    std::string strSysCmd = cmdStream.str();
    zucInnerDbg("CopyFile: from %s to %s\n", src.c_str(), dstDir.c_str());
    return system(strSysCmd.c_str());
}

int32_t DiagOSAPI::CopyDir(const std::string& src, const std::string& dest)
{
    std::string strSysCmd = std::string("cp -rf ") + src + " " + dest;
    zucInnerDbg("CopyDir: %s\n", strSysCmd.c_str());
    return system(strSysCmd.c_str());
}

int32_t DiagOSAPI::RemoveF(const std::string& dirpath)
{
    std::string strSysCmd = std::string("rm -rf ") + dirpath;
    int32_t ret = system(strSysCmd.c_str());
    if (ret != 0)
    {
        zucInnerDbg("Failed to RmDir: %s\n", strSysCmd.c_str());
    }
    else
    {
        zucInnerDbg(strSysCmd.c_str());
    }
    return ret;
}

int32_t DiagOSAPI::OSInfo(std::ostream& ss)
{
    utsname info;
    int32_t ret = uname(&info);
    if (__builtin_expect(ret != 0, 0))
    {
        return -1;
    }

    ss << "OS Name: " << info.sysname << "\n";
    ss << "Node Name: " << info.nodename << "\n";
    ss << "Release: " << info.release << "\n";
    ss << "Version: " << info.version << "\n";
    ss << "Machine: " << info.machine << "\n";
    return 0;
}

int32_t DiagOSAPI::PackTargz(const std::string& curWorkspaceDir, const std::string& packName, const std::string& dirName)
{
    std::string sysCmd = "cd " + curWorkspaceDir + "\n";
    sysCmd += "tar -zcvf " + packName + ".tar.gz " + dirName + " --remove-files";
    zucInnerDbg("PackTargz: %s\n", sysCmd.c_str());
    return system(sysCmd.c_str());
}

int32_t DiagOSAPI::FileSize(const std::string& path, uint64_t& sz)
{
    struct stat st;
    if (stat(path.c_str(), &st) != 0)
    {
        return -1;
    }

    if (!S_ISREG(st.st_mode))
    {
        return -1;
    }

    sz = st.st_size;
    return 0;
}

int32_t DiagOSAPI::TraverseDir(const std::string& dirpath, std::queue<FilePairType>& q)
{
    struct dirent* entry;
    DIR* dir = opendir(dirpath.c_str());
    if (!dir)
    {
        return -1;
    }

    while (true)
    {
        entry = readdir(dir);
        if (entry == nullptr)
        {
            break;
        }

        const std::string name = entry->d_name;
        const unsigned char type = entry->d_type;
        FilePairType pair = std::make_pair(dirpath + '/' + name, type);

        if (type != DT_DIR && type != DT_REG)
        {
            continue;
        }

        if (name == "." || name == "..")
        {
            continue;
        }

        q.push(pair);
    }

    closedir(dir);

    return 0;
}

void DiagOSAPI::ListFileCBFunc::operator()(const DiagOSAPI::FilePairType& filepair)
{
    if (filepair.second == DT_REG)
    {
        filelist.push_back(filepair.first);
    }
}

void DiagOSAPI::ListDirCBFunc::operator()(const DiagOSAPI::FilePairType& filepair)
{
    if (filepair.second == DT_DIR)
    {
        pathlist.push_back(filepair.first);
    }
}

int32_t DiagOSAPI::TopCpuLog(std::ostream& topLog)
{
    const std::string cmd = "top -b -n1 -o %CPU";
    int32_t ret = Popen(cmd, topLog);
    return ret;
}

int32_t DiagOSAPI::TopMemLog(std::ostream& topLog)
{
    const std::string cmd = "top -b -n1 -o %MEM";
    int32_t ret = Popen(cmd, topLog);
    return ret;
}

int32_t DiagOSAPI::IOTopDiskLog(std::ostream& topLog)
{
    const std::string cmd = "iostat -d 1 1";
    int32_t ret = Popen(cmd, topLog);
    return ret;
}

int32_t DiagOSAPI::DumpSysLog(std::ostream& sysLog, uint32_t hour, uint32_t lineNum)
{
    const std::string cmd = std::string(ZUC_DEFAULT_USER_BIN_DIR) + "/shutils dumpLog " + std::to_string(hour) + " " + std::to_string(lineNum);
    int32_t ret = Popen(cmd, sysLog);
    return ret;
}

int32_t DiagOSAPI::Popen(const std::string& cmd, std::ostream& out)
{
    char buf[1024];
    FILE* pipeFd = ::popen(cmd.c_str(), "r");
    if (!pipeFd)
    {
        zucInnerDbg("Popen: (%s)\n", ::strerror(errno));
        return -1;
    }

    while (fgets(buf, sizeof(buf), pipeFd)) { out << buf; }

    return pclose(pipeFd) ? -1 : 0;
}
